﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CloudClipboard
{
    public static class CloudClipboardHelper
    {
        [DllImport("user32.dll")]
        private static extern bool AddClipboardFormatListener(IntPtr hwnd);

        [DllImport("user32.dll")]
        private static extern bool RemoveClipboardFormatListener(IntPtr hwnd);

        public static int WM_CLIPBOARDUPDATE = 0x031D;

        public static SystemConfigInfo Config { get; private set; }

        public static IntPtr MainFromHandle { get; private set; }

        public static void Initialize(IntPtr mainFromHandle)
        {
            CloudClipboardHelper.MainFromHandle = mainFromHandle;

            string serverHost = ConfigurationManager.AppSettings["ServerHost"];
            string clientGroup = ConfigurationManager.AppSettings["ClientGroup"];
            int serverPort = 0;

            if (string.IsNullOrWhiteSpace(System.Configuration.ConfigurationManager.AppSettings["ServerPort"]) == false)
            {
                int.TryParse(ConfigurationManager.AppSettings["ServerPort"], out  serverPort);
            }

            CloudClipboardHelper.Config = new SystemConfigInfo(serverHost, serverPort, clientGroup);
        }

        public static void RemoveClipboardFormatListener()
        {
            CloudClipboardHelper.RemoveClipboardFormatListener(CloudClipboardHelper.MainFromHandle);
        }

        public static void AddClipboardFormatListener()
        {
            CloudClipboardHelper.AddClipboardFormatListener(CloudClipboardHelper.MainFromHandle);
        }

        public static void SetClipboardData(ClipboardDataInfo oClipboardDataInfo)
        {
            if (oClipboardDataInfo == null)
            {
                return;
            }

            try
            {
                CloudClipboardHelper.RemoveClipboardFormatListener();

                if (oClipboardDataInfo.ClipboardDataType == ClipboardDataTypes.Text)
                {
                    string text = System.Text.Encoding.UTF8.GetString(oClipboardDataInfo.Data);
                    Clipboard.SetData(DataFormats.Text, text);
                    Debug.WriteLine("设置文本类型剪贴板成功");
                }
                //else if (oClipboardDataInfo.ClipboardDataType == ClipboardDataTypes.Html)
                //{
                //    string text = System.Text.Encoding.UTF8.GetString(oClipboardDataInfo.Data);
                //    Clipboard.SetData(DataFormats.Html, text);
                //    Debug.WriteLine("设置Html类型剪贴板成功");
                //}
                else if (oClipboardDataInfo.ClipboardDataType == ClipboardDataTypes.Bitmap)
                {
                    MemoryStream stream = null;
                    System.Drawing.Bitmap bmp = null;
                    try
                    {
                        stream = new MemoryStream(oClipboardDataInfo.Data);
                        bmp = new System.Drawing.Bitmap(stream);
                    }
                    catch (ArgumentNullException ex)
                    {
                    }
                    catch (ArgumentException ex)
                    {
                    }
                    finally
                    {
                        stream.Close();
                    }

                    if (bmp != null)
                    {
                        Clipboard.SetData(DataFormats.Bitmap, bmp);
                        Debug.WriteLine("设置图片类型剪贴板成功");
                    }
                    else
                    {
                        Debug.WriteLine("设置图片类型剪贴板失败");
                    }
                }

                CloudClipboardHelper.AddClipboardFormatListener();
            }
            catch (Exception)
            {
            }
        }

        public static bool CheckIsSupportDataType() {
            try
            {
                IDataObject iData = Clipboard.GetDataObject();
                return iData.GetDataPresent(DataFormats.Text) || iData.GetDataPresent(DataFormats.Bitmap);
            }
            catch
            {
                return false;
            }
        }

        public static byte[] GetClipboardData()
        {
            try
            {
                IDataObject iData = Clipboard.GetDataObject();

                //if (iData.GetDataPresent(DataFormats.Html))
                //{
                //    string sData = (string)iData.GetData(DataFormats.Html);
                //    if (string.IsNullOrWhiteSpace(sData))
                //    {
                //        return null;
                //    }

                //    ClipboardDataInfo oClipboardDataInfo = new ClipboardDataInfo();
                //    oClipboardDataInfo.GroupHashCode = CloudClipboardHelper.Config.GroupHashCode;
                //    oClipboardDataInfo.ClipboardDataType = ClipboardDataTypes.Html;
                //    oClipboardDataInfo.Data = System.Text.Encoding.UTF8.GetBytes(sData);
                //    oClipboardDataInfo.IsCompress = oClipboardDataInfo.Data.Length > 1024;

                //    return CloudClipboardHelper.SerializeClipboardData(oClipboardDataInfo);
                //}
                //else 
                if (iData.GetDataPresent(DataFormats.Text))
                {
                    string sData = (string)iData.GetData(DataFormats.Text);
                    if (string.IsNullOrWhiteSpace(sData))
                    {
                        return null;
                    }

                    ClipboardDataInfo oClipboardDataInfo = new ClipboardDataInfo();
                    oClipboardDataInfo.GroupHashCode = CloudClipboardHelper.Config.GroupHashCode;
                    oClipboardDataInfo.ClipboardDataType = ClipboardDataTypes.Text;
                    oClipboardDataInfo.Data = System.Text.Encoding.UTF8.GetBytes(sData);
                    oClipboardDataInfo.IsCompress = oClipboardDataInfo.Data.Length > 1024;

                    return CloudClipboardHelper.SerializeClipboardData(oClipboardDataInfo);
                }
                else if (iData.GetDataPresent(DataFormats.Bitmap))
                {
                    System.Drawing.Bitmap bmp = iData.GetData(DataFormats.Bitmap) as System.Drawing.Bitmap;
                    if (bmp == null)
                    {
                        return null;
                    }

                    byte[] bytes = null;

                    using (MemoryStream ms = new MemoryStream())
                    {
                        bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                        // bmp.Save(ms, bmp.RawFormat);
                        bytes = ms.ToArray();
                        //ms.Position = 0;
                        // bytes = new byte[ms.Length];
                        // ms.Read(bytes, 0, bytes.Length);
                    }

                    ClipboardDataInfo oClipboardDataInfo = new ClipboardDataInfo();
                    oClipboardDataInfo.GroupHashCode = CloudClipboardHelper.Config.GroupHashCode;
                    oClipboardDataInfo.ClipboardDataType = ClipboardDataTypes.Bitmap;
                    oClipboardDataInfo.Data = bytes;
                    oClipboardDataInfo.IsCompress = bytes.Length > 1024;

                    return CloudClipboardHelper.SerializeClipboardData(oClipboardDataInfo);
                }
                else if (iData.GetDataPresent(DataFormats.FileDrop))
                {
                    object obj = iData.GetData(DataFormats.FileDrop);
                    return null;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception)
            {
                return null;
            }
        }


        public static byte[] SerializeClipboardData(ClipboardDataInfo oClipboardDataInfo)
        {
            if (oClipboardDataInfo == null)
            {
                return null;
            }

            //客户哈希值
            byte[] clientGroupHashCodeBytes = BitConverter.GetBytes(CloudClipboardHelper.Config.GroupHashCode);
            //数据类型
            UInt16 clipboardDataType = (UInt16)oClipboardDataInfo.ClipboardDataType;
            byte[] dataTypeBytes = BitConverter.GetBytes(clipboardDataType);
            //是否压缩
            byte[] isCompressBytes = null;
            //数据长度ss
            byte[] dataLengthBytes = null;
            //数据
            byte[] dataBytes = null;

            if (oClipboardDataInfo.IsCompress)
            {
                UInt16 isCompress = 1;
                isCompressBytes = BitConverter.GetBytes(isCompress);

                dataBytes = CloudClipboardHelper.Compress(oClipboardDataInfo.Data);

                UInt32 dataLength = (UInt32)dataBytes.Length;
                dataLengthBytes = BitConverter.GetBytes(dataLength);

                Debug.WriteLine(string.Format("压缩{0}/{1}={2:N2}", dataBytes.Length, oClipboardDataInfo.Data.Length, (double)dataBytes.Length / (double)oClipboardDataInfo.Data.Length));

            }
            else
            {
                UInt16 isCompress = 0;
                isCompressBytes = BitConverter.GetBytes(isCompress);

                dataBytes = oClipboardDataInfo.Data;

                UInt32 dataLength = (UInt32)oClipboardDataInfo.Data.Length;
                dataLengthBytes = BitConverter.GetBytes(dataLength);
            }

            MemoryStream ms = new MemoryStream();
            ms.Seek(0, SeekOrigin.Begin);

            //客户端哈希值
            ms.Write(clientGroupHashCodeBytes, 0, clientGroupHashCodeBytes.Length);
            //数据类型
            ms.Write(dataTypeBytes, 0, dataTypeBytes.Length);
            //是否压缩
            ms.Write(isCompressBytes, 0, isCompressBytes.Length);
            //数据长度
            ms.Write(dataLengthBytes, 0, dataLengthBytes.Length);
            //数据
            ms.Write(dataBytes, 0, dataBytes.Length);

            ms.Seek(0, SeekOrigin.Begin);

            byte[] returnBytes = new byte[ms.Length];
            ms.Read(returnBytes, 0, returnBytes.Length);
            ms.Close();

            return returnBytes;
        }

        public static ClipboardDataInfo DeSerializeClipboardData(byte[] data)
        {
            if (data == null)
            {
                return null;
            }

            if (data.Length < (sizeof(UInt32) + sizeof(UInt16) + sizeof(UInt16) + sizeof(UInt32)))
            {
                return null;
            }

            ClipboardDataInfo oClipboardDataInfo = new ClipboardDataInfo();

            oClipboardDataInfo.GroupHashCode = BitConverter.ToUInt32(data, 0);
            oClipboardDataInfo.ClipboardDataType = (ClipboardDataTypes)BitConverter.ToUInt16(data, sizeof(UInt32));
            oClipboardDataInfo.IsCompress = BitConverter.ToUInt16(data, sizeof(UInt32) + sizeof(UInt16)) == 1;

            UInt32 dataLength = BitConverter.ToUInt32(data, sizeof(UInt32) + sizeof(UInt16) + sizeof(UInt16));

            if (data.Length == (dataLength + sizeof(UInt32) + sizeof(UInt16) + sizeof(UInt16) + sizeof(UInt32)))
            {
                oClipboardDataInfo.Data = new byte[dataLength];
                Array.Copy(data, sizeof(UInt32) + sizeof(UInt16) + sizeof(UInt16) + sizeof(UInt32), oClipboardDataInfo.Data, 0, dataLength);

                if (oClipboardDataInfo.IsCompress)
                {
                    oClipboardDataInfo.Data = CloudClipboardHelper.Decompress(oClipboardDataInfo.Data);
                }
            }

            return oClipboardDataInfo;
        }

        public static byte[] SerializeCmd(UInt32 cmd, byte[] data)
        {
            MemoryStream ms = new MemoryStream();
            byte[] cmdBytes = BitConverter.GetBytes(cmd);
            ms.Write(cmdBytes, 0, cmdBytes.Length);

            if (data != null)
            {
                ms.Write(data, 0, data.Length);
            }

            ms.Seek(0, SeekOrigin.Begin);

            byte[] returnBytes = new byte[ms.Length];
            ms.Read(returnBytes, 0, returnBytes.Length);
            ms.Close();

            return returnBytes;
        }

        public static byte[] DeSerializeCmd(out UInt32 cmd, byte[] data)
        {
            if (data == null && data.Length >= sizeof(UInt32))
            {
                cmd = 0;
                return null;
            }

            cmd = BitConverter.ToUInt32(data, 0);
            byte[] returnBytes = new byte[data.Length - sizeof(UInt32)];
            Array.Copy(data, sizeof(UInt32), returnBytes, 0, data.Length - sizeof(UInt32));

            return returnBytes;
        }

        // 压缩
        public static byte[] Compress(byte[] rawData)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                GZipStream compressedzipStream = new GZipStream(ms, CompressionMode.Compress, true);
                compressedzipStream.Write(rawData, 0, rawData.Length);
                compressedzipStream.Close();
                return ms.ToArray();
            }
        }

        // 解压
        public static byte[] Decompress(byte[] rawData)
        {
            using (MemoryStream tempMs = new MemoryStream())
            {
                using (MemoryStream ms = new MemoryStream(rawData))
                {
                    GZipStream Decompress = new GZipStream(ms, CompressionMode.Decompress);
                    Decompress.CopyTo(tempMs);
                    Decompress.Close();
                    return tempMs.ToArray();
                }
            }
        }

        public static void LoopCall(int loopTimes, TimeSpan timeSpan, Func<int, bool> fun)
        {
            ThreadPool.QueueUserWorkItem((obj) =>
            {
                for (int i = 1; loopTimes <= 0 ? true : i <= loopTimes; i++)
                {
                    Thread.Sleep(timeSpan);
                    if (false == (bool)MainForm.Current.Invoke(fun, i))
                    {
                        break;
                    }
                }
            });
        }
    }
}
